package com.leyou.auth.inter.util;

import com.leyou.auth.inter.pojo.UserInfo;
import io.jsonwebtoken.*;
import org.joda.time.DateTime;

import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class JwtUtils {
    /**
     * 私钥加密token
     *
     * @param userInfo      载荷中的数据
     * @param privateKey    私钥
     * @param expireMinutes 过期时间，单位秒
     * @return
     * @throws Exception
     */
    public static String generateToken(UserInfo userInfo, PrivateKey privateKey, int expireMinutes) throws Exception {
        return Jwts.builder()
                .claim(JwtConstans.JWT_KEY_ID, userInfo.getId())
                .claim(JwtConstans.JWT_KEY_USER_NAME, userInfo.getUsername())
                .claim(JwtConstans.JWT_KEY_ROLES, userInfo.getRoles())
                .setExpiration(DateTime.now().plusMinutes(expireMinutes).toDate())
                .signWith(SignatureAlgorithm.RS256, privateKey)
                .compact();
    }

    /**
     * 私钥加密token
     *
     * @param userInfo      载荷中的数据
     * @param privateKey    私钥字节数组
     * @param expireMinutes 过期时间，单位秒
     * @return
     * @throws Exception
     */
    public static String generateToken(UserInfo userInfo, byte[] privateKey, int expireMinutes) throws Exception {
        return Jwts.builder()
                .claim(JwtConstans.JWT_KEY_ID, userInfo.getId())
                .claim(JwtConstans.JWT_KEY_USER_NAME, userInfo.getUsername())
                .setExpiration(DateTime.now().plusMinutes(expireMinutes).toDate())
                .signWith(SignatureAlgorithm.RS256, RsaUtils.getPrivateKey(privateKey))
                .compact();
    }

    /**
     * 公钥解析token
     *
     * @param token     用户请求中的token
     * @param publicKey 公钥
     * @return
     * @throws Exception
     */
    private static Jws<Claims> parserToken(String token, PublicKey publicKey) {
        return Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token);
    }

    /**
     * 公钥解析token
     *
     * @param token     用户请求中的token
     * @param publicKey 公钥字节数组
     * @return
     * @throws Exception
     */
    private static Jws<Claims> parserToken(String token, byte[] publicKey) throws Exception {
        return Jwts.parser().setSigningKey(RsaUtils.getPublicKey(publicKey))
                .parseClaimsJws(token);
    }

    /**
     * 获取token中的用户信息
     *
     * @param token     用户请求中的令牌
     * @param publicKey 公钥
     * @return 用户信息
     * @throws Exception
     */
    public static UserInfo getInfoFromToken(String token, PublicKey publicKey) throws Exception {
        Jws<Claims> claimsJws = parserToken(token, publicKey);
        Claims body = claimsJws.getBody();
        return new UserInfo(
                ObjectUtils.toLong(body.get(JwtConstans.JWT_KEY_ID)),
                ObjectUtils.toString(body.get(JwtConstans.JWT_KEY_USER_NAME)),
                ObjectUtils.toString(body.get(JwtConstans.JWT_KEY_ROLES))
        );
    }

    /**
     * 获取token中的用户信息
     *
     * @param token     用户请求中的令牌
     * @param publicKey 公钥
     * @return 用户信息
     * @throws Exception
     */
    public static UserInfo getInfoFromToken(String token, byte[] publicKey) throws Exception {
        Jws<Claims> claimsJws = parserToken(token, publicKey);
        Claims body = claimsJws.getBody();
        return new UserInfo(
                ObjectUtils.toLong(body.get(JwtConstans.JWT_KEY_ID)),
                ObjectUtils.toString(body.get(JwtConstans.JWT_KEY_USER_NAME)),
                ObjectUtils.toString(body.get(JwtConstans.JWT_KEY_ROLES))
        );
    }

    /* JWT签发者 */
    private static final String ISS = "leyou";

    /**
     * @Title: createJWT
     * @Description: 生成token
     * @author: zqq
     * @param userInfo 用户信息
     * @param privateKey 私钥
     * @param expireMinutes 过期时间，单位分
     * @return: java.lang.String
     * @createTime: 2020-03-27 17:09
     * @version: v.1.0
     */
    public static String createJWT(UserInfo userInfo, PrivateKey privateKey, int expireMinutes) throws Exception {
        Long ttlMillis = 1000L * expireMinutes * 60;
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.RS256; //指定签名的时候使用的签名算法，也就是header那部分，jjwt已经将这部分内容封装好了。
        long nowMillis = System.currentTimeMillis();//生成JWT的时间
        Date now = new Date(nowMillis);
        Map<String,Object> claims = new HashMap<>();
        claims.put(JwtConstans.JWT_KEY_ID, userInfo.getId());
        claims.put(JwtConstans.JWT_KEY_USER_NAME, userInfo.getUsername());
        claims.put(JwtConstans.JWT_KEY_ROLES,userInfo.getRoles());
        claims.put(JwtConstans.JWT_KEY_FLAG,userInfo.getIsFlag());
        claims.put("iss",ISS);
        JwtBuilder builder = Jwts.builder()
                .setClaims(claims)
                .setId(userInfo.getId().toString())     //设置jti(JWT ID)：是JWT的唯一标识，根据业务需要，这个可以设置为一个不重复的值，主要用来作为一次性token,从而回避重放攻击。
                .setIssuedAt(now)           //iat: jwt的签发时间
                .setSubject(userInfo.getId().toString())        //sub(Subject)：代表这个JWT的主体，即它的所有人，这个是一个json格式的字符串，可以存放什么userid，roldid之类的，作为什么用户的唯一标志。
                .signWith(signatureAlgorithm, privateKey);//设置签名使用的签名算法和签名使用的秘钥
        if (ttlMillis >= 0) {
            long expMillis = nowMillis + ttlMillis;
            Date exp = new Date(expMillis);
            builder.setExpiration(exp);     //设置过期时间
        }
        return builder.compact();
    }

    /**
     * @Title: validParseJWT
     * @Description: 验证并解析token
     * @author: zqq
     * @param token
     * @param publicKey 公钥
     * @return: java.util.Map<java.lang.String,java.lang.Object>
     * @createTime: 2020-03-27 17:11
     * @version: v.1.0
     */
    public static Map<String,Object> validParseJWT(String token, PublicKey publicKey){
        Map<String,Object> map = new HashMap<>();
        Claims claims = null;
        try {
            claims = Jwts.parser()
                    .setSigningKey(publicKey)
                    .parseClaimsJws(token).getBody();
        } catch (ExpiredJwtException e) {
            map.put("state",TokenState.EXPIRED);
            return map;
        } catch (SignatureException e) {
            map.put("state",TokenState.SIGNATURE);
            return map;
        } catch (Exception e) {
            map.put("state",TokenState.ANALYSIS_EXCEPTION);
            return map;
        }
        String iss = claims.get("iss", String.class);
        if(!ISS.equals(iss)){
            map.put("state",TokenState.ISS_EXCEPTION);
            return map;
        }
        UserInfo userInfo = new UserInfo(
                ObjectUtils.toLong(claims.get(JwtConstans.JWT_KEY_ID)),
                ObjectUtils.toString(claims.get(JwtConstans.JWT_KEY_USER_NAME)),
                ObjectUtils.toString(claims.get(JwtConstans.JWT_KEY_ROLES)),
                ObjectUtils.toBoolean(claims.get(JwtConstans.JWT_KEY_FLAG)));
        map.put("userInfo",userInfo);
        map.put("state",TokenState.VALID);
        return map;
    }



}